#!/usr/bin/env python
# -*- coding: utf-8 -*-

import errno
import json
import zmq
from typing import Dict, Any
from common.config import ServiceConfig
from common.utils import LogUtil
from web_server.models.service_info import ServiceInfo

logger = LogUtil.get_logger(__name__)

class ZmqClient:
    def __init__(self, service_info: ServiceInfo, zmq_syn_context, zmq_aio_context):
        self.__service_info = service_info
        self.__zmq_syn_context = zmq_syn_context
        self.__zmq_aio_context = zmq_aio_context
        self.__rep_socket_address = f"tcp://{self.__service_info.host}:{self.__service_info.rep_port}"
        self.__pub_socket_address = f"tcp://{self.__service_info.host}:{self.__service_info.pub_port}"
        self.__aio_server_address = f"tcp://{self.__service_info.host}:{self.__service_info.aio_port}"
        self.__req_socket_address = f"tcp://{ServiceConfig.ZMQ_CLIENT_HOST}:0"
        self.__sub_socket_address = f"tcp://{ServiceConfig.ZMQ_CLIENT_HOST}:0"
        self.__aio_client_address = f"tcp://{ServiceConfig.ZMQ_CLIENT_HOST}:0"
        
        self.__req_socket = None
        self.__sub_socket = None
        self.__aio_socket = None

        self.__reset_req_socket()
        self.__reset_sub_socket()
        self.__reset_aio_socket()
        
        logger.info("ZmqClient初始化完成")

    def __reset_req_socket(self):
        if self.__req_socket:
            self.__req_socket.close()
            self.__req_socket = None
        self.__req_socket = self.__zmq_syn_context.socket(zmq.REQ)
        self.__req_socket.bind(self.__req_socket_address)
        self.__req_socket.setsockopt(zmq.RCVTIMEO, ServiceConfig.ZMQ_TIMEOUT)
        self.__req_socket.setsockopt(zmq.SNDTIMEO, ServiceConfig.ZMQ_TIMEOUT)
        self.__req_socket.connect(self.__rep_socket_address)

    def __reset_sub_socket(self):
        if self.__sub_socket:
            self.__sub_socket.close()
            self.__sub_socket = None
        self.__sub_socket = self.__zmq_syn_context.socket(zmq.SUB)
        self.__sub_socket.bind(self.__sub_socket_address)
        self.__sub_socket.setsockopt_string(zmq.SUBSCRIBE, 'reload_devices')
        self.__sub_socket.connect(self.__pub_socket_address)

    def __reset_aio_socket(self):
        if self.__aio_socket:
            self.__aio_socket.close()
            self.__aio_socket = None
        self.__aio_socket = self.__zmq_aio_context.socket(zmq.REQ)
        self.__aio_socket.bind(self.__aio_client_address)
        self.__aio_socket.setsockopt(zmq.RCVTIMEO, ServiceConfig.ZMQ_TIMEOUT)
        self.__aio_socket.setsockopt(zmq.SNDTIMEO, ServiceConfig.ZMQ_TIMEOUT)
        self.__aio_socket.connect(self.__aio_server_address)

    def __del__(self):
        self.destroy()

    def destroy(self):
        if self.__req_socket:
            self.__req_socket.close()
            self.__req_socket = None
        if self.__sub_socket:
            self.__sub_socket.close()
            self.__sub_socket = None
        if self.__aio_socket:
            self.__aio_socket.close()
            self.__aio_socket = None

    def send_json(self, request: Dict):
        try:
            self.__req_socket.send_json(request)
            logger.debug(f"发送UiTest请求成功")
            return True
        except zmq.error.Again as e:
            # logger.debug(f"发送UiTest请求超时: {e}")
            pass
        except zmq.error.ZMQError as e:
            if e.errno == errno.EINTR:
                logger.debug(f"发送UiTest请求被信号中断")
            else:
                logger.error(f"发送UiTest请求发生ZMQ错误: {e}")
                self.__reset_req_socket()
        except Exception as e:
            logger.error(f"发送UiTest请求发生未知错误: {e}")
            self.__reset_req_socket()
        return False

    async def async_send_json(self, request):
        try:
            await self.__aio_socket.send_json(request)
            logger.info(f"发送信令消息成功")
            return True
        except zmq.error.Again:
            logger.info(f"发送信令消息超时")
            pass
        except zmq.error.ZMQError as e:
            if e.errno == errno.EINTR:
                logger.info(f"发送信令消息被信号中断")
            else:
                logger.error(f"发送信令消息发生ZMQ错误: {e}")
                self.__reset_aio_socket()
        except Exception as e:
            logger.error(f"发送信令消息发生未知错误: {e}")
            self.__reset_aio_socket()
        return False

    def recv_json(self) -> Dict[str, Any]:
        response = None
        while True:
            try:
                response = self.__req_socket.recv_json()
                logger.debug(f"接收UiTest响应成功")
                break
            except zmq.error.Again as e:
                # logger.debug(f"接收UiTest响应超时: {e}")
                continue
            except zmq.error.ZMQError as e:
                if e.errno == errno.EINTR:
                    logger.debug(f"接收UiTest响应被信号中断")
                    continue
                else:
                    logger.error(f"接收UiTest响应发生ZMQ错误: {e}")
                    self.__reset_req_socket()
                    break
            except json.JSONDecodeError as e:
                logger.error(f"解析UiTest响应失败: {e}")
                break
            except Exception as e:
                logger.error(f"接收UiTest响应发生未知错误: {e}")
                self.__reset_req_socket()
                break
        return response

    async def async_recv_json(self):
        response = None
        while True:
            try:
                response = await self.__aio_socket.recv_json()
                logger.info(f"接收信令消息成功")
                break
            except zmq.error.Again as e:
                # logger.info(f"接收信令消息超时: {e}")
                continue
            except zmq.error.ZMQError as e:
                if e.errno == errno.EINTR:
                    logger.info(f"接收信令消息被信号中断")
                    continue
                else:
                    logger.error(f"接收WebRTC消息发生ZMQ错误: {e}")
                    self.__reset_aio_socket()
                    break
            except json.JSONDecodeError as e:
                logger.error(f"解析WebRTC消息失败: {e}")
                break
            except Exception as e:
                logger.error(f"接收WebRTC消息发生未知错误: {e}")
                self.__reset_aio_socket()
                break
        return response

    def recv_multipart(self):
        response = None
        try:
            response = self.__sub_socket.recv_multipart(zmq.NOBLOCK)
            logger.info(f"接收设备事件成功")
        except zmq.error.Again as e:
            # logger.debug(f"接收消息超时: {e}")
            return response
        except zmq.error.ZMQError as e:
            if e.errno == errno.EINTR:
                logger.info(f"接收设备事件被信号中断")
            else:
                logger.error(f"接收设备事件发生ZMQ错误: {e}")
                self.__reset_sub_socket()
        except Exception as e:
            logger.error(f"接收设备事件失败: {e}")
            self.__reset_sub_socket()
        return response
